/*

 Redis操作类封装

 对ioredis模块进行操作的二次封装

*/

const ioredis = require('ioredis')

class Redis {
	
	constructor(common, config) {

		this.common = common
		let { host, port, pwd, db, name } = config

		//新建一个redis连接实例
		this.redis = new ioredis({
			host,
			port,
			db,
			password: pwd,
			connectionName: name,
			retryStrategy: 3000
		})

		//监听redis连接事件
		this.redis.on('connect', () => {
			console.log(`redis db ${name} is connected`)
			common.writeLog('info', `redis db ${name} is connected`)
		})

		//监听redis连接关闭事件
		this.redis.on('close', () => {
			console.warn(`redis db ${name} is closed`)
			common.writeLog('warn', `redis db ${name} is closed`)
		})

		//监听redis连接错误事件
		this.redis.on('error', (err) => {
			let {code, syscall} = err
			let msg = ''
			switch(code) {
				case 'ECONNREFUSED':
					msg = `redis server not running`
					common.alertMsg('Redis连接挂啦', msg)
				break
				default:
					console.error(err)
					msg = `redis db ${name} ${syscall} failed - ${code}`
			}
			console.error(msg)
			common.writeLog('error', msg)
		})

	}

	resolve(data = 0) {
		return Promise.resolve(data)
	}


	reject(code, msg) {
		return Promise.reject({code, msg})
	}

	//建立redis服务器连接
	connect() {
		this.redis.connect()
	}

	//断开redis服务器连接
	disconnect() {
		this.redis.disconnect()
	}

	/*
	
	 获取redis变量值
	 
	 @param [string] key 变量的名称
	 @param [boolean] json 是否将获取的值转换成对象
	 
	 @return [string/object] 变量值 如果json参数为true则会尝试转换后再返回
	 
	*/

	async get(key, json = false) {
		try {
			let val = await this.redis.get(key)
			if(json) {
				val = JSON.parse(val)
			}
			return this.resolve(val)
		}
		catch(err) {
			console.error(err)
			return this.reject('-1012', 'get redis data failed')
		}
	}

	/*
	
	 设置redis变量
	
	 @param [string] key 变量的名称
	 @param [number/string/boolen] val 变量的值
	 @param [number] expire 变量的生命周期（秒）

	 @return [boolean] 设置值是否成功
	
	*/

	async set(key, val, expire) {
		try {
			if(!await this.common.isString(val) && !await this.common.isNum(val)) {
				val = JSON.stringify(val)
			}
			let result
			if(expire) {
				 result = await this.redis.set(key, val, 'EX', expire)
			}
			else {
				 result = await this.redis.set(key, val)
			}
			return this.resolve(result == 'OK')
		}
		catch(err) {
			console.error(err)
			return this.reject('-1013', 'set redis data failed')
		}
	}

	/*
	
	 删除redis变量
	 
	 @param [string] key 变量的名称

	 @return [number] 被删除的条数
	 
	*/

	async delete(key) {
		try {
			return this.resolve(await this.redis.del(key))
		}
		catch(err) {
			console.error(err)
			return this.reject('-1014', 'delete redis data failed')
		}
	}

	/*
	
	 设置redis变量生命周期
	
	 @param [string] key 变量的名称
	 @param [number] expire 变量的生命周期（秒）默认半小时

	 @return [boolean] 设置有效期是否成功
	
	*/

	async expire(key, expire = 1800) {
		try {
			const val = await this.get(key)
			const result = await this.set(key, val, 'EX', expire)
			return this.resolve(result == 'OK')
		}
		catch(err) {
			console.error(err)
			return this.reject('-1015', 'change redis data expire failed')
		}
	}

	/*

	 扫描redis库内的key

	 @param [string] pattern 匹配key的表达式
	 @param [number] count 每次扫描的key个数（不可靠但由slice保证输出个数）

	 @return [array] 扫描到的key数组

	*/

	scan(pattern, count = null) {
		return new Promise( async (resolve, reject) => {
			try {
				if(!pattern)
					return []
				let keys = []
				const stream = this.redis.scanStream({
					match: pattern,
					count
				})
				stream.on('data', (result) => {
					for(let key of result)
						keys.push(key)
				})
				stream.on('end', () => {
					//如果设置了count参数由于scanStream不保证每次都返回在count范围内的key数量
					//所以由slice保证key数组元素个数
					if(count)
						keys = keys.slice(0, count)
					resolve(keys)
				})
			}
			catch(err) {
				console.error(err)
				reject('-1016', 'scan redis data failed')
			}
		})
	}

}

module.exports = Redis